package config

import (
	"fmt"
	"gopkg.in/yaml.v3"
	"os"
	"sync"
	"wzgames/pkg/utils/stringutil"
)

type YAMLConfig struct {
}

func (yc *YAMLConfig) Load(file string) (ConfData, error) {
	// File exists?
	if _, err := os.Stat(file); os.IsNotExist(err) {
		return nil, err
	}
	data, err := os.ReadFile(file)
	if err != nil {
		return nil, err
	}
	return yc.Parse(data)
}

func (yc *YAMLConfig) Parse(data []byte) (ConfData, error) {
	o := &YAMLConfData{
		data: make(map[string]interface{}),
	}
	err := yaml.Unmarshal(data, &o.data)
	if err != nil {
		return nil, err
	}

	return o, nil
}

type YAMLConfData struct {
	data map[string]interface{}
	sync.RWMutex
}

func (yo *YAMLConfData) ReplaceData(d map[string]interface{}) {
	yo.Lock()
	defer yo.Unlock()

	yo.data = d
}

func (yo *YAMLConfData) getData(key string) interface{} {
	yo.Lock()
	defer yo.Unlock()

	if v, ok := yo.data[key]; ok {
		return v
	}
	return nil
}

func (yo *YAMLConfData) GetString(key string, defaultVal ...string) string {
	val := yo.getData(key)
	if val != nil {
		if v, ok := val.(string); ok {
			return v
		}
	}
	if len(defaultVal) > 0 {
		return defaultVal[0]
	}
	return ""
}

func (yo *YAMLConfData) GetInt(key string, defaultVal ...int) int {
	val := yo.getData(key)
	if val != nil {
		if v, ok := val.(int); ok {
			return v
		} else if v, ok := val.(int64); ok {
			return int(v)
		}
	}
	if len(defaultVal) > 0 {
		return defaultVal[0]
	}
	return 0
}

func (yo *YAMLConfData) GetInt64(key string, defaultVal ...int64) int64 {
	val := yo.getData(key)
	if val != nil {
		if v, ok := val.(float64); ok {
			return int64(v)
		}
	}
	if len(defaultVal) > 0 {
		return defaultVal[0]
	}
	return 0
}

func (yo *YAMLConfData) GetBool(key string) (bool, error) {
	val := yo.getData(key)
	if val != nil {
		return stringutil.ToBoolean(val)
	}
	return false, fmt.Errorf("[Config] yaml not exist key: %q", key)
}

func (yo *YAMLConfData) GetFloat64(key string, defaultVal ...float64) float64 {
	val := yo.getData(key)
	if val != nil {
		if v, ok := val.(float64); ok {
			return v
		} else if v, ok := val.(int); ok {
			return float64(v)
		} else if v, ok := val.(int64); ok {
			return float64(v)
		}
	}
	if len(defaultVal) > 0 {
		return defaultVal[0]
	}
	return 0.0
}

func (yo *YAMLConfData) GetRawValue(key string) interface{} {
	val := yo.getData(key)
	if val != nil {
		return val
	}
	return nil
}

func (yo *YAMLConfData) SetRawValue(key string, val interface{}) {
	yo.Lock()
	defer yo.Unlock()
	yo.data[key] = val
}

func (yo *YAMLConfData) ForEachKey(fn func(string, interface{})) {
	yo.Lock()
	defer yo.Unlock()
	for k, v := range yo.data {
		fn(k, v)
	}
}

func (yo *YAMLConfData) SaveFile(file string) error {
	// Write configuration file by filename.
	f, err := os.Create(file)
	if err != nil {
		return err
	}
	defer f.Close()
	b, err := yaml.Marshal(yo.data)
	if err != nil {
		return err
	}
	_, err = f.Write(b)
	return err
}

func init() {
	Register("yaml", &YAMLConfig{})
}
